import base64
import hashlib
import hmac
import json
import time  # for nonce

import requests  # pip install requests

import coin.exchange.base.kr_rest.rest_client_base as rcb
from coin.exchange.base.kr_rest.auth_base import AuthKey
from coin.exchange.util.rate_limit import RateLimiter


class BitfinexPublicClientBase(object):
  def __init__(self, key_file):
    key = AuthKey.from_file(key_file)
    self.KEY = key.access_key
    self.SECRET = key.secret_key_bytes
    self.BASE_URL = "https://api.bitfinex.com/"
    self.rate_limit = RateLimiter(3, 1.0)
    self.timeout = 5

  def _nonce(self):
    return str(int(round(time.time() * 1000000)))

  def _sign_payload(self, payload):
    data = base64.standard_b64encode(payload.encode('utf8'))
    h = hmac.new(self.SECRET, data, hashlib.sha384)
    signature = h.hexdigest()
    return {
        "X-BFX-APIKEY": self.KEY,
        "X-BFX-SIGNATURE": signature,
        "X-BFX-PAYLOAD": data,
    }

  def _query_impl(self, path, params=None):
    if params is None:
      params = {}

    assert path.startswith("/")
    path = "/v1" + path
    assert path.startswith("/")
    params["request"] = path
    params["nonce"] = self._nonce()

    rawBody = json.dumps(params)

    signed_payload = self._sign_payload(rawBody)
    r = requests.post(self.BASE_URL + path,
                      headers=signed_payload,
                      verify=True,
                      timeout=self.timeout)

    if r.status_code == 200:
      return r.json()
    else:
      print(r.status_code)
      print(r.json())
      print(r)
      return ''

  def _query_nocheck(self, endpoint, params):
    req_timestamp = time.time()
    content = self._query_impl(endpoint, params)
    res_timestamp = time.time()
    return rcb.RestUpdate(req_timestamp, res_timestamp, content)

  def _query(self, endpoint, params=None, disable_rate_limit=False):
    if not disable_rate_limit:
      while not self.rate_limit.check():
        time.sleep(0.1)
    else:
      self.rate_limit.check()
    return self._query_nocheck(endpoint, params=params)


class BitfinexNativePrivateClient(BitfinexPublicClientBase):
  def __init__(self, key_file):
    BitfinexPublicClientBase.__init__(self, key_file)

  ############################################################################
  # Account
  ############################################################################

  def query_account_infos(self):
    return self._query("/account_infos")

  def query_account_fees(self):
    return self._query("/account_fees")

  def query_deposit_new(self):
    raise NotImplementedError()

  def query_key_info(self):
    return self._query("/key_info")

  def query_margin_infos(self):
    return self._query("/margin_infos")

  def query_balances(self):
    return self._query("/balances")

  def query_transfer(self):
    raise NotImplementedError()

  def query_withdraw(self):
    raise NotImplementedError()

  ############################################################################
  # Order
  ############################################################################

  def query_order_new(self, symbol, side, price, amount, order_type):
    # assert symbol in currency_list
    assert type(price) == float
    assert type(amount) == float
    assert side in ["buy", "sell"]
    # assert order_type in ["market", "limit", "stop", "trailing-stop", "fill-or-kill"]
    assert order_type in \
      ["exchange market", "exchange limit", "exchange stop", "exchange trailing-stop", "exchange fill-or-kill"]

    exchange = 'bitfinex'
    payload = {
        "symbol": symbol,
        "side": side,
        "amount": str(amount),
        "price": str(price),
        "type": order_type,
        "exchange": exchange,
        "is_hidden": False,
        "is_postonly": False,
        "use_all_available": 0,
        "ocoorder": False,
        "buy_price_oco": 0,
        "sell_price_oco": 0,
    }

    return self._query("/order/new", payload)
    # json_resp['order_id']

  def query_order_new_multi(self):
    raise NotImplementedError()

  def query_order_cancel(self, order_id):
    payload = {"order_id": order_id}
    return self._query("/order/cancel", payload)
    # json_resp['avg_execution_price']

  def query_order_cancel_multi(self):
    raise NotImplementedError()

  def query_order_cancel_all(self):
    return self._query("/order/cancel/all")

  def query_order_cancel_replace(self):
    raise NotImplementedError()

  def query_order_status(self, order_id):
    payload = {"order_id": order_id}
    return self._query("/order/status", payload)
    # json_resp['avg_execution_price']

  def query_orders(self):
    return self._query("/orders")

  def query_order_hist(self):
    return self._query("/order/hist")

  ############################################################################
  ## FROM OTHERS. CLEAN IT UP
  ############################################################################

  def query_trades(self):
    raise NotImplementedError()

  def query_positions(self):
    return self._query("/auth/r/positions")

  def active_positions(self):
    return self._query("/positions")

  def claim_position(self, position_id):
    payload = {"position_id": position_id}
    return self._query("/position/claim", payload)

  def past_trades(self, timestamp=0, symbol='btcusd'):
    payload = {"symbol": symbol, "timestamp": timestamp}
    return self._query("/mytrades", payload)

  def place_offer(self, currency, amount, rate, period, direction):
    payload = {
        "currency": currency,
        "amount": amount,
        "rate": rate,
        "period": period,
        "direction": direction
    }
    return self._query("/offer/new", payload)

  def cancel_offer(self, offer_id):
    payload = {"offer_id": offer_id}
    return self._query("/offer/cancel", payload)

  def status_offer(self, offer_id):
    payload = {"offer_id": offer_id}
    return self._query("/offer/status", payload)

  def active_offers(self):
    return self._query("/offers")

  def history(self, currency, since=0, until=9999999999, limit=500, wallet='exchange'):
    payload = {
        "currency": currency, "since": since, "until": until, "limit": limit, "wallet": wallet
    }
    return self._query("/history", payload)
